#include<stdio.h>
#include<sys/epoll.h>
#include<error.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<sys/socket.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<pthread.h>
#include<sys/stat.h>
#include<sys/sendfile.h>
#include<fcntl.h>
#include<malloc.h>



#define SIZE 10240
//请求头的数据结构
struct Request  
{
  char first_line[SIZE];
  char* method;
  char* url;
  char* url_path;
  char* query_string;
  int content_length;
  Request()
    :method(NULL)
     ,url(NULL)
     ,url_path(NULL)
     ,query_string(NULL)
     ,content_length(0)
  {}
};

typedef struct Request Request;

//创建一个可用的socket
int ServerInit(int port)
{
  printf("port:%d\n",port);
  int sock = socket(AF_INET,SOCK_STREAM,0);  
  if(sock<0)
  {
    perror("socket");
    return -1;
  }
  struct sockaddr_in local;
  local.sin_family = AF_INET;
  local.sin_addr.s_addr = INADDR_ANY;
  local.sin_port = htons(port);
  if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
  {
    perror("bind");
    return -1;
  }
  if(listen(sock,1000)<0)
  {
    perror("listen");
    return -1;
  }

  return sock;
}


//添加读事件到efd中
int AddEpollevent(int efd,int sock,epoll_event* ev)
{
  if(epoll_ctl(efd,EPOLL_CTL_ADD,sock,ev) < 0)
  {
    printf("AddEpollevent failed\n");
    return -1;
  }
  return 1;
}

//1.从就绪的文件描述符中读取请求的一行
int  GetLine(int sock,char* first_line,int size)
{
  char c = 'a';
  int i = 0;
  while(c != '\n' && i< size-1)
  {
    recv(sock,&c,1,0);
    if(c == '\r') 
    {
      //窥探下一个字符，并不取走
      recv(sock,&c,1,MSG_PEEK);
      if(c == '\n')
        recv(sock,&c,1,0);
      else 
        c = '\n';
    }
    first_line[i++] = c;
  }
  first_line[i] = '\0';
  return i;
}

//按照要求切分字符串
int Split(char* first_line,const char* src,char** tok,int size)
{
  int i = 0;
  char* tmp;
  char* cur = strtok_r(first_line,src,&tmp);
  while(cur)
  {
    if(i>size)
      return -1;
    tok[i++] = cur;
    cur = strtok_r(NULL,src,&tmp);
  }
  return i;
}

//2.解析出首行的method和Url
int ParseFirstLine(char*first_line,char** method,char** url)
{
  char* tok[10];
  int sp_size = Split(first_line," ",tok,10); 
  if(sp_size != 3)
  {
    printf("Split failed! : sp_size=%d\n",sp_size);
    return -1;
  }
  *method = tok[0];
  *url = tok[1];
  return 0;
}
//3.从URl中解析出url_path和query_string
int ParseUrl(char* url,char** url_path,char** query_string)
{
  *url_path = url;
  char* tmp = url;
  while(*tmp != '?' && *tmp != ' ')
  {
    if(*tmp == '\0' || *tmp == ' ')
    {
      //没有query_string
      *query_string = NULL;
      return -1;
    }
    tmp++;
  }
  *tmp = '\0';
  *query_string = tmp+1;
  return 0;
}



//处理错误
void HandlerError404(int sock)
{
  const char* first_line = "HTTP/1.1 404 Not Fount\n";
  send(sock,first_line,strlen(first_line),0);
  const char* type_line = "Content-Type: text/html; charset=utf-8\n";
  send(sock,type_line,strlen(type_line),0);
  const char* blank_line = "\n";
  send(sock,blank_line,strlen(blank_line),0);
  const char* body_line = "<head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"></head> \
                           <h1>页面找不到了！</h1>"; 
                           send(sock,body_line,strlen(body_line),0);
}

//处理请求头部，提取出content-length
int ParseHead(int sock,int*content_length)
{
  char line[SIZE];
  const char *tmp = "Content-Length: ";
  while(GetLine(sock,line,SIZE))
  {
    if(strncmp(line,tmp,strlen(tmp)) == 0)
    {
      *content_length = atoi(line+strlen(tmp));
      continue;
    }
    else if(strcmp(line,"\n") == 0) //读到空行，头部提取完毕
      return 0;
  }
  content_length = 0;
  return 1;
}

void PrintRequest(Request &req)
{
  printf("----------------------------\n");
  printf("method : %s\n",req.method);
  printf("url_path: %s\n",req.url_path);
  printf("query_string:%s\n",req.query_string);
  printf("content_length:%d\n",req.content_length);
  printf("----------------------------\n");
}

//判断文件是否是一个目录
bool IsDir(char* file_path)
{
  struct stat st;
  int ret = stat(file_path,&st);
  if(ret < 0)
  {
    perror("stat");
    return false;
  }
  if(S_ISDIR(st.st_mode))
  {
    return true;
  }
  return false;
}

//把客户请求的资源路径变为服务器的真实路径
void  HandlerFilePath(char* & url_path,char* file_path)
{
  sprintf(file_path,"./wwwroot%s",url_path); 
  //如果客户给的路径是个 / ,则直接在最后加上index.html
  if(file_path[strlen(file_path)-1] == '/')
  {
    strcat(file_path,"index.html");
  }
  else if(IsDir(file_path))
  {
    strcat(file_path,"/index.html");;
  }
}

int get_size(char*file_path)
{
  struct stat st;
  if(stat(file_path,&st)<0)
    return -1;
  return st.st_size;
}

//返回静态页面
int WriteStaticPage(int sock,char* file_path)
{
  int fd = open(file_path,O_RDONLY); 
  if(fd<0)
  {
    printf("open failed\n");
    return 404;
  }
  //构造响应头部，写回sock
  const char *first_line = "HTTP/1.1 OK 200\n";
  send(sock,first_line,strlen(first_line),0);
  const char *blank_line = "\n";
  send(sock,blank_line,strlen(blank_line),0);

  int size = get_size(file_path);
  if(size<0)
  {
    printf("get_size failed\n");
    return 404;
  }
  sendfile(sock,fd,NULL,size);

  close(fd);
  return 200;
}
//GET方法，并且不带参数，返回一个html静态页面
int HandlerStaticPage(int sock,Request& req)
{
  char file_path[SIZE] = {0};
  //把客户请求的资源路径变为服务器的真实路径
  HandlerFilePath(req.url_path,file_path);
  printf("source path : %s\n",file_path);
  //返回静态页面
  return WriteStaticPage(sock,file_path);
}

void HandlerCGI(int sock,Request& req)
{

}

//处理事件
void  HandlerEvent(int sock)
{
  int err_code = 200;
  Request req;
  //1.读取请求的首行
  if(GetLine(sock,req.first_line,sizeof(req.first_line)) < 0)
  {
    err_code = 404;
    goto END;
  }
  //2.解析出首行的method和Url
  if( ParseFirstLine(req.first_line,&req.method,&req.url)<0)
  {
    err_code = 404;
    goto END;
  }
  //3.从URl中解析出url_path和query_string
  ParseUrl(req.url,&req.url_path,&req.query_string);


  //4.读取sock中的剩余行，并提取出content-length
  if(ParseHead(sock,&req.content_length) < 0)
  {
    err_code = 404;
    goto END;
  }

  //打印提取出请求的内容
  PrintRequest(req);

  //5.根据获取的方法进行处理
  //  GET 并且没有带参数
  if(strcmp(req.method,"GET") == 0 && req.query_string == NULL ) 
  {
    printf("GET but no query_string\n");
    //GET方法，并且不带参数，返回一个html静态页面
    err_code = HandlerStaticPage(sock,req);
  }
  else if(strcmp(req.method,"GET") == 0 && req.query_string != NULL)
  {
    //GET方法并且带参数
    HandlerCGI(sock,req);
  }  
  //  POST
  else if(strcmp(req.method,"POST") == 0)
  {
    //POST方法
    HandlerCGI(sock,req);
  }
  else
  {
    //其他方法，不处理
    err_code = 404;
    goto END;
  }


  //错误处理
END:
  if(err_code != 200 )
  {
    HandlerError404(sock);
  }
  close(sock);
  return;
}

//使用epoll监听事件
void  MyEpoll(int  listen_sock)
{
  //创建epoll的结构
  int efd = epoll_create(10);
  if(efd<0)
  {
    perror("epoll_create");
    return;
  }

  //添加第一次的新链接事件
  struct epoll_event ev;
  ev.data.fd = listen_sock;
  ev.events = EPOLLIN;

  //添加读事件到efd中
  int ret = AddEpollevent(efd,listen_sock,&ev);
  if(ret<0)
  {
    printf("AddEpollevent failed\n");
    return;
  }
  //循环监听efd中的就绪事件
  while(1)
  {
    printf("开始处理事件\n");
    //检测事件是否就绪
    struct epoll_event output_events[100];
    printf("start epoll_wait\n");
    int ret_fd = epoll_wait(efd,output_events,sizeof(output_events)/sizeof(output_events[0]),-1);
    printf("epoll_wait end:%d\n",ret_fd);
    if(ret_fd<0) //epoll_wait出错
    {
      perror("epoll_wait");
      continue;
    }
    else if(ret_fd == 0) //超时了
    {
      printf("epoll_wait timeout!\n");
      continue;
    }
    else // 有事件就绪
    {
      for(int i = 0; i<ret_fd; i++)
      {
        //来了一个新连接
        if(output_events[i].data.fd == listen_sock)
        {
          int new_sock = accept(output_events[i].data.fd,NULL,NULL);
          if(new_sock<0)
          {
            perror("accept");
            continue;
          }

          struct epoll_event ev;
          ev.data.fd = new_sock;
          ev.events = EPOLLIN;
          int ret = AddEpollevent(efd,new_sock,&ev);
          if(ret<0)
          {
            printf("AddEpollevent failed\n");
            return;
          }
          printf("get a new client\n");
        }
        else{
          //处理就绪的描述符
          HandlerEvent(output_events[i].data.fd);
          printf("HandlerEvent end : %d\n",output_events[i].data.fd);
        }
      }
    }

  }//end while

}

int main(int argc,char*argv[])
{
  if(argc != 2)
  {
    printf("Usage:[./server] [port]\n");
    return -1;
  }


  //初始化服务器，产生一个被动套接字文件描述符
  int listen_sock = ServerInit(atoi(argv[1]));
  if(listen_sock<0)
  {
    printf("httpinit failed\n");
    return -1;
  }
  printf("socket 准备完成\n");
  //使用epoll监听事件
  MyEpoll(listen_sock);

  return 0;
}

